import { App, DropdownComponent, Notice, PluginSettingTab, Setting, SliderComponent, TextComponent, ToggleComponent } from "obsidian";
import JupyterNotebookPlugin from "./jupyter-obsidian";
import { JupyterEnvironmentStatus, JupyterEnvironmentType } from "./jupyter-env";
import { JupyterRestartModal } from "./ui/jupyter-restart-modal";

export enum PythonExecutableType {
    PYTHON = "python",
    PATH = "path"
}

export enum OpenCreatedNotebook {
    DONT = "dont-open",
    CURRENT_TAB = "current-tab",
    NEW_TAB = "new-tab",
    SPLIT = "split",
    WINDOW = "detached-window"
}

export interface JupyterSettings {
    pythonExecutable: PythonExecutableType;
    pythonExecutablePath: string;
    startJupyterAuto: boolean;
    jupyterEnvType: JupyterEnvironmentType;
    useSimpleMode: boolean;
    deleteCheckpoints: boolean;
    moveCheckpointsToTrash: boolean;
    /**
     * Required to end with '/'. When this value is set, a check
     * is performed, and '/' is added at the end if needed.
     */
    checkpointsFolder: string;
    updatePopup: boolean;
    displayServerRibbonIcon: boolean;
    useStatusNotices: boolean;
    displayFileRibbonIcon: boolean;
    displayFolderContextMenuItem: boolean;
    openCreatedFileMode: OpenCreatedNotebook,
    jupyterTimeoutMs: number;
    debugConsole: boolean;

    // These are not for the user to modify
    knownVersion: string;
};
export const DEFAULT_SETTINGS: JupyterSettings = {
    pythonExecutable: PythonExecutableType.PYTHON,
    pythonExecutablePath: "",
    startJupyterAuto: true,
    jupyterEnvType: JupyterEnvironmentType.LAB,
    useSimpleMode: true,
    deleteCheckpoints: false,
    moveCheckpointsToTrash: true,
    checkpointsFolder: "",
    updatePopup: true,
    displayServerRibbonIcon: true,
    useStatusNotices: true,
    displayFileRibbonIcon: true,
    displayFolderContextMenuItem: true,
    openCreatedFileMode: OpenCreatedNotebook.CURRENT_TAB,
    jupyterTimeoutMs: 30000,
    debugConsole: false,

    knownVersion: ""
};

export class JupyterSettingsTab extends PluginSettingTab {
    constructor(app: App, private plugin: JupyterNotebookPlugin) {
        super(app, plugin);
    }

    display() {
        this.containerEl.empty();


        /*=====================================================*/
	    /* Python settings                                     */
	    /*=====================================================*/

        new Setting(this.containerEl)
            .setName("Python")
            .setHeading();
        new Setting(this.containerEl)
            .setName("Python executable to use")
            .setDesc("Choose whether to simply use the `python` command or a specific path. Note that you will need to restart your Jupyter server if it is running before this setting is applied.")
            .addDropdown(((dropdown: DropdownComponent) => {
                dropdown
                    .addOption(PythonExecutableType.PYTHON, "`python` command")
                    .addOption(PythonExecutableType.PATH, "Specified executable path")
                    .setValue(this.plugin.settings.pythonExecutable)
                    .onChange((async (value: PythonExecutableType) => {
                        await this.plugin.setPythonExecutable(value);
                    }).bind(this));
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Python executable path")
            .setDesc("The path to the Python executable to use. This setting is only used if the previous setting is set to `Specified executable path`.")
            .addText(((text: TextComponent) => {
                text
                    .setPlaceholder("Path to Python executable")
                    .setValue(this.plugin.settings.pythonExecutablePath)
                    .onChange((async (value: string) => {
                        await this.plugin.setPythonExecutablePath(value);
                    }).bind(this));
            }).bind(this));


        /*=====================================================*/
	    /* Jupyter settings                                    */
	    /*=====================================================*/

        new Setting(this.containerEl)
            .setName("Jupyter")
            .setHeading();
        new Setting(this.containerEl)
            .setName("Server running")
            .setDesc("Start or stop the Jupyter server.")
            .addToggle(((toggle: ToggleComponent) =>
                toggle
                    .setValue(this.plugin.env.getStatus() !== JupyterEnvironmentStatus.EXITED)
                    .onChange(((value: boolean) => {
                        if (this.plugin.env.getStatus() === JupyterEnvironmentStatus.STARTING && !value) {
                            toggle.setValue(true);
                            new Notice("Can't change status while Jupyter server is starting.");
                        }
                        else {
                            this.plugin.toggleJupyter();
                        }
                    }).bind(this))
            ).bind(this));
        new Setting(this.containerEl)
            .setName("Start Jupyter automatically")
            .setDesc("If a .ipynb file is opened, a Jupyter server will be started automatically if needed.")
            .addToggle(((toggle: ToggleComponent) => {
                toggle
                    .setValue(this.plugin.settings.startJupyterAuto)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setStartJupyterAuto(value);
                    }).bind(this))
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Jupyter environment type")
            .setDesc("Select whether to start Jupyter Notebook or Jupyter Lab.")
            .addDropdown(((dropdown: DropdownComponent) => {
                dropdown
                    .addOption(JupyterEnvironmentType.LAB, "Jupyter Lab")
                    .addOption(JupyterEnvironmentType.NOTEBOOK, "Jupyter Notebook")
                    .setValue(this.plugin.settings.jupyterEnvType)
                    .onChange((async (value: JupyterEnvironmentType) => {
                        await this.plugin.setJupyterEnvType(value);

                        if (this.plugin.env.getStatus() !== JupyterEnvironmentStatus.EXITED) {
                            new JupyterRestartModal(this.plugin, "Jupyter environment type").open();
                        }
                    }).bind(this));
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Simple interface")
            .setDesc("Whether to use Jupyter's Simple Interface mode when opening a notebook.")
            .addToggle(((toggle: ToggleComponent) => {
                toggle
                    .setValue(this.plugin.settings.useSimpleMode)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setUseSimpleMode(value);
                    }).bind(this))
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Delete Jupyter checkpoints")
            .setDesc("To keep your Obsidian vault clean. Does not work retroactively. Restarting Jupyter is required for the setting to take effect.")
            .addToggle(((toggle: ToggleComponent) => {
                toggle
                    .setValue(this.plugin.settings.deleteCheckpoints)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setDeleteCheckpoints(value);

                        if (this.plugin.env.getStatus() !== JupyterEnvironmentStatus.EXITED) {
                            new JupyterRestartModal(this.plugin, "Delete Jupyter checkpoints").open();
                        }
                    }).bind(this))
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Move Jupyter checkpoints to trash")
            .setDesc("Has no effect if 'Delete Jupyter checkpoints' is not enabled. If enabled, checkpoints are moved to system trash. Otherwise, they are permanently deleted.")
            .addToggle(((toggle: ToggleComponent) => {
                toggle
                    .setValue(this.plugin.settings.moveCheckpointsToTrash)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setMoveCheckpointsToTrash(value);
                    }).bind(this))
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Jupyter checkpoints folder")
            .setDesc("The root folder for all Jupyter checkpoints. Leave empty for default. Requires restarting Jupyter to take effect. Has no effect if 'Delete Jupyter checkpoints' is not enabled.")
            .addText(((text: TextComponent) => {
                text
                    .setPlaceholder(this.plugin.getDefaultCheckpointsRootFolder().getAbsolutePath() ?? "No default path available")
                    .setValue(this.plugin.settings.checkpointsFolder)
                    .onChange((async (value: string) => {
                        await this.plugin.setCheckpointsFolder(value);
                    }).bind(this));
            }).bind(this));


        /*=====================================================*/
	    /* Plugin customization settings                       */
	    /*=====================================================*/

        new Setting(this.containerEl)
            .setName("Plugin customization")
            .setHeading();
        new Setting(this.containerEl)
            .setName("Update popup")
            .setDesc("When the plugin is updated, a popup is shown with what changes were made.")
            .addToggle(((toggle: ToggleComponent) => {
                toggle
                    .setValue(this.plugin.settings.updatePopup)
                    .onChange(((value: boolean) => {
                        void this.plugin.setUpdatePopup(value);
                    }).bind(this));
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Ribbon icon for server status")
            .setDesc("Whether to display a ribbon icon that indicates the server status (exited, starting, running), which can be used to start/stop the server.")
            .addToggle(((toggle: ToggleComponent) =>
                toggle
                    .setValue(this.plugin.settings.displayServerRibbonIcon)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setServerRibbonIconSetting(value);
                    }).bind(this))
            ).bind(this));
        new Setting(this.containerEl)
            .setName("Display status notices")
            .setDesc("If enabled, short messages will pop up when the Jupyter server is starting, running or exits.")
            .addToggle(((toggle: ToggleComponent) =>
                toggle
                    .setValue(this.plugin.settings.useStatusNotices)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setStatusNoticesSetting(value);
                    }).bind(this))
            ).bind(this));
        new Setting(this.containerEl)
            .setName("Ribbon icon for new notebooks")
            .setDesc("Whether to display a ribbon icon that creates a blank Jupyter notebook when clicked.")
            .addToggle(((toggle: ToggleComponent) =>
                toggle
                    .setValue(this.plugin.settings.displayFileRibbonIcon)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setFileRibbonIconSetting(value);
                    }).bind(this))
            ).bind(this));
        new Setting(this.containerEl)
            .setName("Folder context menu for new notebooks")
            .setDesc("If enabled, when you right-click on a folder, one of the actions will be to create a new Jupyter notebook in that folder.")
            .addToggle(((toggle: ToggleComponent) =>
                toggle
                    .setValue(this.plugin.settings.displayFolderContextMenuItem)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setFolderContextMenuSetting(value);
                    }).bind(this))
            ).bind(this));
        new Setting(this.containerEl)
            .setName("Open created notebooks")
            .setDesc("Whether to open a notebook directly when it is created, and how to open it.")
            .addDropdown(((dropdown: DropdownComponent) => {
                dropdown
                    .addOption(OpenCreatedNotebook.DONT, "Do not open")
                    .addOption(OpenCreatedNotebook.CURRENT_TAB, "Open in the current tab (default)")
                    .addOption(OpenCreatedNotebook.NEW_TAB, "Open in a new tab")
                    .addOption(OpenCreatedNotebook.SPLIT, "Open in a new split tab")
                    .addOption(OpenCreatedNotebook.WINDOW, "Open in a detached window")
                    .setValue(this.plugin.settings.openCreatedFileMode)
                    .onChange((async (value: OpenCreatedNotebook) => {
                        await this.plugin.setOpenCreatedFileMode(value);
                    }).bind(this));
            }).bind(this));


        /*=====================================================*/
	    /* Advanced settings                                   */
	    /*=====================================================*/

        new Setting(this.containerEl)
            .setName("Advanced")
            .setHeading();
        new Setting(this.containerEl)
            .setName("Jupyter starting timeout")
            .setDesc("To avoid Jupyter being stuck in the starting phase, a timeout is set by default. You can set how many seconds to wait before killing the Jupyter server. Set to 0 to disable the timeout. Please note that a timeout too small might prevent Jupyter from ever starting.")
            .addSlider(((slider: SliderComponent) => {
                slider
                    .setLimits(0, 60, 1)
                    .setValue(this.plugin.settings.jupyterTimeoutMs / 1000)
                    .setDynamicTooltip()
                    .onChange((async (value: number) => {
                        await this.plugin.setJupyterTimeoutMs(value * 1000);
                    }).bind(this));
            }).bind(this));
        new Setting(this.containerEl)
            .setName("Print Jupyter output to Obsidian console.")
            .setDesc("When you start Jupyter through a terminal, it prints a bunch of messages. You can get those messages in the Obsidian console by enabling this setting and opening the console (see key binds on the Obsidian website). This can help you if your Jupyter server does not start for some reason.")
            .addToggle(((toggle: ToggleComponent) => {
                toggle
                    .setValue(this.plugin.settings.debugConsole)
                    .onChange((async (value: boolean) => {
                        await this.plugin.setDebugConsole(value);
                    }).bind(this))
            }).bind(this));
    }
}